package com.flyqiu.flow.core.impl.ru;


import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.flyqiu.common.tools.FlyQiuCollectionTools;
import com.flyqiu.common.tools.StringTools;
import com.flyqiu.common.transaction.FlyQiuTransactionManage;
import com.flyqiu.common.user.FlyQiuUserTools;
import com.flyqiu.flow.api.context.FlyQiuFlowGlobalContext;
import com.flyqiu.flow.api.script.FlyQiuFlowVarSerializeManage;
import com.flyqiu.flow.api.service.ru.FlowRuVariableService;
import com.flyqiu.flow.common.enums.VarScopeType;
import com.flyqiu.flow.mapper.ProcRuVariableMapper;
import com.flyqiu.flow.model.entity.base.Variable;
import com.flyqiu.flow.model.entity.ru.ProcRuVariable;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

public class FlowRuVariableServiceImpl extends ServiceImpl<ProcRuVariableMapper, ProcRuVariable> implements FlowRuVariableService {
    private final FlyQiuFlowGlobalContext context;
    private final FlyQiuFlowVarSerializeManage varSerializeManage;
    private final FlyQiuTransactionManage transactionManage;

    public FlowRuVariableServiceImpl(FlyQiuFlowGlobalContext context) {
        this.context = context;
        transactionManage = context.getTransactionManage();
        ProcRuVariableMapper mapper = context.getBeanManage().getMapper(ProcRuVariableMapper.class);
        varSerializeManage = context.getFlowVarSerializeManage();
        this.baseMapper = mapper;
    }

    @Override
    public List<ProcRuVariable> getProcVariables(String procInstId) {
        LambdaQueryWrapper<ProcRuVariable> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ProcRuVariable::getProcInstId, procInstId);
        queryWrapper.eq(ProcRuVariable::getVarScope, VarScopeType.PROCESS.getValue());
        return list(queryWrapper);
    }

    @Override
    public Map<String, ProcRuVariable> getProcVarablesMap(String procInstId) {
        return getProcVariables(procInstId).stream().collect(Collectors.toMap(Variable::getVarName, e -> e));
    }


    @Override
    public List<ProcRuVariable> getProcActVariables(String procInstId, String actId) {
        LambdaQueryWrapper<ProcRuVariable> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ProcRuVariable::getProcInstId, procInstId);
        queryWrapper.eq(ProcRuVariable::getActId, actId);
        queryWrapper.eq(ProcRuVariable::getVarScope, VarScopeType.ACT.getValue());
        return list(queryWrapper);
    }

    @Override
    public Map<String, ProcRuVariable> getProcActVarablesMap(String procInstId, String actId) {
        return getProcActVariables(procInstId, actId).stream().collect(Collectors.toMap(Variable::getVarName, e -> e));
    }

    @Override
    public void addProcVariables(String procInstId, JSONObject varMap) {
        if (StringTools.isEmpty(procInstId)) return;
        if (FlyQiuCollectionTools.isEmpty(varMap)) return;
        Map<String, ProcRuVariable> oldProcVarablesMap = getProcVarablesMap(procInstId);
        List<ProcRuVariable> nowVariables = mapToVariables(varMap, VarScopeType.PROCESS.getValue(), procRuVariable -> {
            procRuVariable.setProcInstId(procInstId);
            return procRuVariable;
        });

        if (FlyQiuCollectionTools.isEmpty(oldProcVarablesMap)) {
            transactionManage.apply(() -> {
                saveBatch(nowVariables);
            });

            return;
        }
        for (ProcRuVariable variable : nowVariables) {
            if (!oldProcVarablesMap.containsKey(variable.getVarName())) {
                continue;
            }
            ProcRuVariable procRuVariable = oldProcVarablesMap.get(variable.getVarName());
            variable.setId(procRuVariable.getId());
        }
        transactionManage.apply(() -> {
            saveOrUpdateBatch(nowVariables);
        });


    }

    @Override
    public void addActVariables(String procInstId, String actId, JSONObject varMap) {
        if (StringTools.isEmpty(procInstId)) return;
        if (FlyQiuCollectionTools.isEmpty(varMap)) return;
        Map<String, ProcRuVariable> oldProcVarablesMap = getProcActVarablesMap(procInstId, actId);
        List<ProcRuVariable> nowVariables = mapToVariables(varMap, VarScopeType.ACT.getValue(), procRuVariable -> {
            procRuVariable.setProcInstId(procInstId);
            procRuVariable.setActInstId(actId);
            return procRuVariable;
        });
        if (FlyQiuCollectionTools.isEmpty(oldProcVarablesMap)) {
            transactionManage.apply(() -> {
                saveBatch(nowVariables);
            });
            return;
        }
        for (ProcRuVariable variable : nowVariables) {
            if (!oldProcVarablesMap.containsKey(variable.getVarName())) {
                continue;
            }
            ProcRuVariable procRuVariable = oldProcVarablesMap.get(variable.getVarName());
            variable.setId(procRuVariable.getId());
        }
        transactionManage.apply(() -> {
            saveOrUpdateBatch(nowVariables);
        });
    }

    @Override
    public void delVariable(String procInstId, String actId, String... varNames) {
        if (StringTools.isAllBlank(procInstId)) {
            throw new RuntimeException("procInstId cannot be null or empty");
        }
        LambdaQueryWrapper<ProcRuVariable> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Variable::getProcInstId, procInstId);
        queryWrapper.eq(StringTools.isNotBlank(actId), Variable::getActId, actId);
        String tenantId = FlyQiuUserTools.getTenantId();
        queryWrapper.eq(ProcRuVariable::getTenantId, tenantId);
        if (varNames.length < 1) {
            remove(queryWrapper);
            return;
        }
        if (varNames.length < 2) {
            queryWrapper.eq(Variable::getVarName, varNames[0]);
        } else {
            queryWrapper.in(Variable::getVarName, Arrays.asList(varNames));
        }
        remove(queryWrapper);
    }

    @Override
    public void delVarByProcInstId(String procInstId) {
        LambdaQueryWrapper<ProcRuVariable> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Variable::getProcInstId, procInstId);
        lambdaQueryWrapper.eq(Variable::getVarScope, VarScopeType.PROCESS.getValue());
        remove(lambdaQueryWrapper);
    }

    @Override
    public void delVarByActInstId(String procInstId, String actId) {
        LambdaQueryWrapper<ProcRuVariable> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Variable::getProcInstId, procInstId);
        lambdaQueryWrapper.eq(Variable::getVarScope, VarScopeType.ACT.getValue());
        lambdaQueryWrapper.eq(Variable::getActId, actId);
        remove(lambdaQueryWrapper);
    }

    private List<ProcRuVariable> mapToVariables(JSONObject varMap, String varScope, Function<ProcRuVariable, ProcRuVariable> call) {
        String tenantId = FlyQiuUserTools.getTenantId();
        return varMap.keySet().stream().map(varName -> {
            Object object = varMap.get(varName);
            ProcRuVariable variable = new ProcRuVariable();
            variable.setVarName(varName);
            variable.setVarValue(varMap.getString(varName));
            String varType = object.getClass().getName();
            variable.setValType(varType);
            variable.setVarScope(varScope);
            variable.setTenantId(tenantId);
            return call.apply(variable);
        }).collect(Collectors.toList());
    }

}
